﻿using System.Collections.Generic;
using System.Linq;

namespace Yuchen.NPA.Core.CompilerService.RegularExpression
{
    internal class NFAExpressionVisitor : ExpressionVisitor
    {
        public State State { get; private set; }

        private readonly Stack<State> _states = new Stack<State>();
        public override void Visit(CharNode node)
        {
            if (node.Type != NodeType.Char)
            {
                return;
            }
            var beginState = new State();
            var endState = new State();
            beginState.AddEdges(new Edge(node.Char), endState);
            _states.Push(beginState);
            _states.Push(endState);
            base.Visit(node);
        }

        public override void Visit(Node node)
        {
            base.Visit(node);
            _states.Peek().IsAccepted = true;
            _states.LastOrDefault().IsStart = true;
            State = _states.LastOrDefault();
        }

        public override void Visit(EscapeNode node)
        {
            State startState = new State();
            var endState = new State();
            State charEndState = null;
            switch (node.Char)
            {
                case 'w':
                    var smallLetters = Enumerable.Range(((byte)'a'), 26).Select(x => (char)x);
                    var largeLetters = Enumerable.Range(((byte)'A'), 26).Select(x => (char)x);
                    var numbers = Enumerable.Range(0, 10).Select(x => x.ToString()[0]);
                    var chars = smallLetters.Union(largeLetters).Union(numbers);

                    foreach (var charNode in chars)
                    {
                        var charStartState = new State();
                        charEndState = new State();
                        charStartState.AddEdges(new Edge(charNode), charEndState);
                        charEndState.AddEdges(new Edge(null), endState);
                        startState.AddEdges(new Edge(null), charStartState);
                    }
                    break;
                case 's':
                    startState.AddEdges(new Edge(' '), endState);
                    break;
                case 'd':
                    foreach (var charNode in Enumerable.Range(0, 10).Select(x => x.ToString()[0]))
                    {
                        var charStartState = new State();
                        charEndState = new State();
                        charStartState.AddEdges(new Edge(charNode), charEndState);
                        charEndState.AddEdges(new Edge(null), endState);
                        startState.AddEdges(new Edge(null), charStartState);
                    }
                    break;
                default:
                    startState.AddEdges(new Edge(node.Char), endState);
                    break;
            }
            _states.Push(startState);
            _states.Push(endState);
        }

        public override void Visit(ConcatNode node)
        {
            base.Visit(node);
            var rightEndState = _states.Pop();
            var rightStartState = _states.Pop();
            var leftEndState = _states.Pop();
            var leftStartState = _states.Pop();
            leftEndState.IsAccepted = false;
            rightStartState.IsStart = false;
            leftEndState.AddEdges(new Edge(null), rightStartState);
            _states.Push(leftStartState);
            _states.Push(rightEndState);
        }

        public override void Visit(SelectionNode node)
        {
            var startState = new State();
            var endState = new State();
            foreach (var nodeChild in node.Children)
            {
                Visit(nodeChild);
                _states.Pop().AddNextState(endState);
                startState.AddEdges(new Edge(null), _states.Pop());
            }
            _states.Push(startState);
            _states.Push(endState);
        }

        public override void Visit(StarNode node)
        {
            base.Visit(node);
            var oprandEndState = _states.Pop();
            var oprandStartState = _states.Pop();

            oprandEndState.IsAccepted = false;
            oprandStartState.IsStart = false;
            var startState = new State()
            {
                IsStart = true
            };
            var endState = new State()
            {
                IsAccepted = true
            };
            startState.AddEdges(new Edge(null), oprandStartState);
            oprandEndState.AddEdges(new Edge(null), endState);
            oprandEndState.AddEdges(new Edge(null), oprandStartState);
            startState.AddEdges(new Edge(null), endState);
            _states.Push(startState);
            _states.Push(endState);
        }

        public override void Visit(UnionNode node)
        {
            base.Visit(node);
            var rightEndState = _states.Pop();
            var rightStartState = _states.Pop();
            var leftEndState = _states.Pop();
            var leftStartState = _states.Pop();
            var startState = new State()
            {
                IsStart = true
            };
            var endState = new State()
            {
                IsAccepted = true
            };
            leftEndState.IsAccepted = false;
            rightStartState.IsStart = false;
            rightEndState.IsAccepted = false;
            leftStartState.IsStart = false;
            startState.AddEdges(new Edge(null), leftStartState);
            leftEndState.AddEdges(new Edge(null), endState);
            startState.AddEdges(new Edge(null), rightStartState);
            rightEndState.AddEdges(new Edge(null), endState);
            _states.Push(startState);
            _states.Push(endState);

        }
    }
}
